package com.lihuacats.bilibili.task.bilibili_task.task;

import com.alibaba.fastjson.JSONObject;
import com.lihuacats.bilibili.task.bilibili_task.config.ConfigProperties;
import com.lihuacats.bilibili.task.bilibili_task.domain.UserData;
import com.lihuacats.bilibili.task.bilibili_task.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * 启动类，程序运行开始的地方
 *
 * @author kurisu
 * @Time 2020-10-13
 */
@Slf4j
@Component
public class BiliStart {
    /**
     * 获取DATA对象
     */
    private final UserData userData = UserData.getInstance();

    /**
     * 获取Config配置的对象
     */
    private final ConfigProperties configProperties;

    /**
     * 访问成功
     */
    private static final String SUCCESS = "0";
    /**
     * 账号未登录，即 Cookie 已失效
     */
    private static final String NOT_LOGGED_IN = "-101";

    @Autowired
    public BiliStart(ConfigProperties configProperties) {
        this.configProperties = configProperties;
    }

    public void start() {
        if (checkEnv()) {
            log.error("💔请配置你的Cookie信息");
            return;
        }
        /* 如果用户账户有效 */
        if (check()) {
            log.warn("【用户名】: {}", StringUtil.hideString(userData.getUname(), 1, 1, '*'));
            log.warn("【硬币】: {}", userData.getMoney());
            log.warn("【经验】: {}", userData.getCurrentExp());
            /* 动态执行task包下的所有java代码 */
            scanTask();
            /* 当用户等级为Lv6时，升级到下一级 next_exp 值为 -- 代表无穷大 */
            String maxLevel = "6";
            if (maxLevel.equals(userData.getCurrentLevel())) {
                log.warn("【升级预计】: 当前等级为: Lv{} ,已经是最高等级", maxLevel);
                log.warn("【温馨提示】: 可在配置文件中关闭每日投币操作");
            } else {
                log.warn("【升级预计】: 当前等级为: Lv{} ,预计升级到下一级还需要: {} 天",
                        userData.getCurrentLevel(), getNextLevel());
            }
            log.info("本次任务运行完毕。");

        }

        // 钉钉
        if (StringUtil.isNotBlank(configProperties.getDingding())) {
            SendDingTalk.send(configProperties.getDingding());
        }

        // server酱
        if (StringUtil.isNotBlank(configProperties.getSckey())) {
            SendServer.send(configProperties.getSckey());
        }
        // Server酱 测试号版
        if (StringUtil.isNotBlank(configProperties.getSendkey())) {
            SendServerChan.send(configProperties.getSendkey());
        }
        // PUSHPLUSTK
        if (StringUtil.isNotBlank(configProperties.getPushplustk())) {
            SendPushPlus.send(configProperties.getPushplustk());
        }
        /* Telegram Bot推送 */
        if (StringUtil.isNotBlank(configProperties.getTelegramBotToken())
                && StringUtil.isNotBlank(configProperties.getTelegramChatId())) {
            SendTelegram.send(configProperties.getTelegramBotToken(), configProperties.getTelegramChatId());
        }
    }

    private void scanTask() {
        List<Class<?>> clazzList = new ArrayList<>();
        TaskRegistry pack = new TaskRegistry() {
            @Override
            public void dealClass(String className) {
                try {
                    Class<?> clazz = Class.forName(className);
                    // 判断类是否实现了接口Task
                    if (Arrays.stream(clazz.getInterfaces()).parallel().anyMatch(taskI -> taskI.equals(Task.class))) {
                        clazzList.add(clazz);
                    }
                } catch (Exception e) {
                    log.error("💔反射获取对象错误 : ", e);
                }
            }
        };
        pack.runTasks();

        clazzList.stream().sorted(Comparator.comparing(Class::getName)).forEach(clazz -> {
            try {
                Constructor<?> constructor = clazz.getConstructor();
                Object object = constructor.newInstance();
                Method method = object.getClass().getMethod("run", ConfigProperties.class);
                method.invoke(object, configProperties);
            } catch (Exception e) {
                log.error("💔反射获取对象错误 : ", e);
            }
        });
    }

    private boolean checkEnv() {
        userData.setCookie(configProperties.getBilijct(), configProperties.getSessdata(), configProperties.getDedeuserid());
        return StringUtil.isAnyBlank(configProperties.getBilijct(), configProperties.getSessdata(), configProperties.getDedeuserid());
    }

    /**
     * 检查用户的状态
     *
     * @return boolean
     * @author kurisu
     * @Time 2020-10-13
     */
    private boolean check() {
        Request.UserAgent = InitUserAgent.getOne();
        JSONObject jsonObject = Request.get("https://api.bilibili.com/x/web-interface/nav");
        JSONObject object = jsonObject.getJSONObject("data");
        String code = jsonObject.getString("code");
        if (SUCCESS.equals(code)) {
            JSONObject levelInfo = object.getJSONObject("level_info");
            /* 用户名 */
            userData.setUname(object.getString("uname"));
            /* 账户的uid */
            userData.setMid(object.getString("mid"));
            /* vip类型 */
            userData.setVipType(object.getString("vipType"));
            /* 硬币数 */
            userData.setMoney(object.getBigDecimal("money"));
            /* 经验 */
            userData.setCurrentExp(levelInfo.getIntValue("current_exp"));
            /* 大会员状态 */
            userData.setVipStatus(object.getString("vipStatus"));
            /* 钱包B币卷余额 */
            userData.setCouponBalance(object.getJSONObject("wallet").getIntValue("coupon_balance"));
            /* 升级到下一级所需要的经验 */
            userData.setNextExp(levelInfo.getString("next_exp"));
            /* 获取当前的等级 */
            userData.setCurrentLevel(levelInfo.getString("current_level"));
            return true;
        }
        if (NOT_LOGGED_IN.equals(code)) {
            log.error("【账户({})已失效💔】：请更新你的Cookie信息", configProperties.getDedeuserid());
            return false;
        }
        return false;
    }

    /**
     * 计算到下一级所需要的天数
     * 由于风控抓的紧，为减少相关 api 的请求次数，会有一天的误差
     *
     * @return int 距离升级到下一等级还需要几天
     * @author kurisu
     * @Time 2020-11-17
     */
    private int getNextLevel() {
        /* 当前经验数 */
        int currentExp = userData.getCurrentExp();
        /* 到达下一级所需要的经验数 */
        int nextExp = Integer.parseInt(userData.getNextExp());
        /* 获取当前硬币数量 */
        int num1 = userData.getMoney().intValue();
        /* 获取配置中每日投币数量 */
        int num2 = configProperties.getCoin();
        /* 避免投币数设置成负数异常 */
        num2 = Math.max(num2, 0);
        /* 实际每日能需要投币数 */
        int num = Math.min(num1, num2);
        /* 距离升级到下一级所需要的天数 */
        int nextNum = 0;
        while (currentExp < nextExp) {
            nextNum += 1;
            num1 += 1;
            currentExp += (15 + num * 10);
            num1 -= num;
            num = Math.min(num1, num2);
        }
        return nextNum;
    }
}
